// import com.alibaba.druid.sql.SQLUtils;
// import com.alibaba.druid.sql.ast.SQLExpr*;
// import com.alibaba.druid.sql.ast.SQLIndex;
// import com.alibaba.druid.sql.ast.SQLIndexDefinition;
// import com.alibaba.druid.sql.ast.SQLName*;
// import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
// import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
// import com.alibaba.druid.sql.ast.statement.*;
// import com.alibaba.druid.sql.dialect.mysql.ast.MySqlObject;
// import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor;
// import com.alibaba.druid.sql.visitor.SQLASTVisitor;

// import java.util.std::list;
#include "MySqlTableIndex.h"
#include "../../../../../sql/ast/SQLIndexDefinition.h"
#include "../../../../../sql/ast/expr/SQLMethodInvokeExpr.h"
#include "../../../../../sql/dialect/mysql/visitor/MySqlASTVisitor.h"
#include "../../../../../sql/visitor/SQLASTVisitor.h"
#include "../../../../../utils/instanceof.h"
#include "../../../../SQLUtils.h"
#include "../../../../ast/statement/SQLColumnDefinition.h"

MySqlTableIndex::MySqlTableIndex()
{
  indexDefinition = SQLIndexDefinition_ptr(new SQLIndexDefinition());
  indexDefinition->setParent(SharedObject(MySqlTableIndex));
}

SQLIndexDefinition_ptr MySqlTableIndex::getIndexDefinition()
{
  return indexDefinition;
}

SQLName_ptr MySqlTableIndex::getName()
{
  return indexDefinition->getName();
}

string_ptr MySqlTableIndex::getIndexType()
{
  return indexDefinition->getType();
}

void MySqlTableIndex::setIndexType(string_ptr indexType)
{
  indexDefinition->setType(indexType);
}

void MySqlTableIndex::setName(SQLName_ptr name)
{
  indexDefinition->setName(name);
}

SQLSelectOrderByItem_list_ptr MySqlTableIndex::getColumns()
{
  return indexDefinition->getColumns();
}

void MySqlTableIndex::addColumn(SQLSelectOrderByItem_ptr column)
{
  if (column != nullptr)
  {
    column->setParent(SharedObject(MySqlTableIndex));
  }
  this->indexDefinition->getColumns()->push_back(column);
}

void MySqlTableIndex::accept0(SQLASTVisitor_ptr visitor)
{
  MySqlASTVisitor_ptr tmp = std::dynamic_pointer_cast<MySqlASTVisitor>(visitor);
  accept0(tmp);
}

void MySqlTableIndex::accept0(MySqlASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(MySqlTableIndex)))
  {
    if (indexDefinition->getName() != nullptr)
    {
      indexDefinition->getName()->accept(visitor);
    }

    auto it_getColumns = indexDefinition->getColumns()->begin();
    for (int i = 0; i < indexDefinition->getColumns()->size(); i++)
    {
      std::advance(it_getColumns, i);
      SQLSelectOrderByItem_ptr item = *it_getColumns;
      if (item != nullptr)
      {
        item->accept(visitor);
      }
    }

    auto it_getCovering = indexDefinition->getCovering()->begin();
    for (int i = 0; i < indexDefinition->getCovering()->size(); i++)
    {
      std::advance(it_getCovering, i);
      SQLName_ptr item = *it_getCovering;
      if (item != nullptr)
      {
        item->accept(visitor);
      }
    }
  }
  visitor->endVisit(SharedObject(MySqlTableIndex));
}

// MySqlTableIndex *MySqlTableIndex::clone()
SQLObject_ptr MySqlTableIndex::clone()
{
  // MySqlTableIndex *x = new MySqlTableIndex();
  // indexDefinition->cloneTo(x->indexDefinition);
  // return x;

  SQLObject_ptr tmp = MySqlTableIndex_ptr(new MySqlTableIndex());
  MySqlTableIndex_ptr clone_this = std::dynamic_pointer_cast<MySqlTableIndex>(tmp);

  indexDefinition->cloneTo(clone_this->indexDefinition);

  return clone_this;
}

BOOL_ptr MySqlTableIndex::applyColumnRename(SQLName_ptr columnName, SQLColumnDefinition *to)
{
  for (SQLSelectOrderByItem_ptr orderByItem : *getColumns())
  {
    SQLExpr_ptr expr = orderByItem->getExpr();
    if (instanceof <SQLExpr, SQLName>(expr) && SQLUtils::nameEquals(std::dynamic_pointer_cast<SQLName>(expr), columnName))
    {
      orderByItem->setExpr(to->getName()->clone());
      return BOOL::TRUE;
    }

    if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr) &&
                       SQLUtils::nameEquals(((std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr))->getMethodName()), columnName->getSimpleName()))
    {
      // More complex when with key length.
      if (1 == (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr))->getArguments()->size())
      {
        auto it_getArguments = std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr)->getArguments()->begin();
        std::advance(it_getArguments, 0);
        if (instanceof <SQLExpr, SQLIntegerExpr>(*it_getArguments))
        {
          if (to->getDataType()->hasKeyLength() &&
              1 == to->getDataType()->getArguments()->size())
          {
            auto it_to_getArguments = to->getDataType()->getArguments()->begin();
            std::advance(it_to_getArguments, 0);
            if (instanceof <SQLExpr, SQLIntegerExpr>(*it_to_getArguments))
            {

              {
                int newKeyLength = (int)(std::dynamic_pointer_cast<SQLIntegerExpr>(*it_to_getArguments))->getNumber();
                int oldKeyLength = (int)(std::dynamic_pointer_cast<SQLIntegerExpr>(*it_getArguments))->getNumber();
                if (newKeyLength > oldKeyLength)
                {
                  // Change name and keep key length.
                  (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr))->setMethodName((to->getName()->getSimpleName()));
                  return BOOL::TRUE;
                }
              }
            }
            // Remove key length.
            orderByItem->setExpr(to->getName()->clone());
            return BOOL::TRUE;
          }
        }
      }
    }
  }
  return BOOL::FALSE;
}

BOOL_ptr MySqlTableIndex::applyDropColumn(SQLName_ptr columnName)
{
  auto it_getColumns = indexDefinition->getColumns()->begin();
  for (int i = indexDefinition->getColumns()->size() - 1; i >= 0; i--)
  {
    std::advance(it_getColumns, i);
    SQLExpr_ptr expr = (*it_getColumns)->getExpr();
    if (instanceof <SQLExpr, SQLName>(expr) && SQLUtils::nameEquals(std::dynamic_pointer_cast<SQLName>(expr), columnName))
    {
      // indexDefinition->getColumns().remove(i);
      if (it_getColumns != indexDefinition->getColumns()->end()) // 最后一个不能删除
        indexDefinition->getColumns()->erase(it_getColumns);     // 删除第i个元素
      return BOOL::TRUE;
    }
    if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr) &&
                       SQLUtils::nameEquals(((std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr))->getMethodName()), columnName->getSimpleName()))
    {
      // indexDefinition->getColumns().remove(i);
      if (it_getColumns != indexDefinition->getColumns()->end()) // 最后一个不能删除
        indexDefinition->getColumns()->erase(it_getColumns);     // 删除第i个元素
      return BOOL::TRUE;
    }
  }
  return BOOL::FALSE;
}

void MySqlTableIndex::addOption(string_ptr name, SQLExpr_ptr value)
{
  indexDefinition->addOption(name, value);
}

SQLExpr_ptr MySqlTableIndex::getOption(string_ptr name)
{
  return indexDefinition->getOption(name);
}

SQLExpr_ptr MySqlTableIndex::getOption(long hash64)
{
  return indexDefinition->getOption(hash64);
}

string_ptr MySqlTableIndex::getDistanceMeasure()
{
  return indexDefinition->getDistanceMeasure();
}

string_ptr MySqlTableIndex::getAlgorithm()
{
  return indexDefinition->getAlgorithm();
}

SQLAssignItem_list_ptr MySqlTableIndex::getOptions()
{
  return indexDefinition->getCompatibleOptions();
}

SQLExpr_ptr MySqlTableIndex::getComment()
{
  return indexDefinition->getOptions()->getComment();
}

void MySqlTableIndex::setComment(SQLExpr_ptr x)
{
  this->indexDefinition->getOptions()->setComment(x);
}

SQLExpr_ptr MySqlTableIndex::getDbPartitionBy()
{
  return indexDefinition->getDbPartitionBy();
}

void MySqlTableIndex::setDbPartitionBy(SQLExpr_ptr x)
{
  indexDefinition->setDbPartitionBy(x);
}

SQLExpr_ptr MySqlTableIndex::getTablePartitions()
{
  return indexDefinition->getTbPartitions();
}

void MySqlTableIndex::setTablePartitions(SQLExpr_ptr x)
{
  indexDefinition->setTbPartitions(x);
}

SQLExpr_ptr MySqlTableIndex::getTablePartitionBy()
{
  return indexDefinition->getTbPartitionBy();
}

void MySqlTableIndex::setTablePartitionBy(SQLExpr_ptr x)
{
  indexDefinition->setTbPartitionBy(x);
}

void MySqlTableIndex::setCovering(SQLName_list_ptr covering)
{
  indexDefinition->setCovering(covering);
}

BOOL_ptr MySqlTableIndex::isGlobal()
{
  return this->indexDefinition->isGlobal();
}

void MySqlTableIndex::setGlobal(BOOL_ptr global)
{
  this->indexDefinition->setGlobal(global);
}

BOOL_ptr MySqlTableIndex::isLocal()
{
  return this->indexDefinition->isLocal();
}

void MySqlTableIndex::setLocal(BOOL_ptr local)
{
  this->indexDefinition->setLocal(local);
}

// @Override
SQLName_list_ptr MySqlTableIndex::getCovering()
{
  return indexDefinition->getCovering();
}

SQLName_ptr MySqlTableIndex::getIndexAnalyzerName()
{
  return indexDefinition->getIndexAnalyzerName();
}

void MySqlTableIndex::setIndexAnalyzerName(SQLName_ptr indexAnalyzerName)
{
  this->indexDefinition->setIndexAnalyzerName(indexAnalyzerName);
}

SQLName_ptr MySqlTableIndex::getQueryAnalyzerName()
{
  return indexDefinition->getQueryAnalyzerName();
}

void MySqlTableIndex::setQueryAnalyzerName(SQLName_ptr queryAnalyzerName)
{
  this->indexDefinition->setQueryAnalyzerName(queryAnalyzerName);
}

SQLName_ptr MySqlTableIndex::getWithDicName()
{
  return indexDefinition->getWithDicName();
}

void MySqlTableIndex::setWithDicName(SQLName_ptr withDicName)
{
  this->indexDefinition->setWithDicName(withDicName);
}

SQLName_ptr MySqlTableIndex::getAnalyzerName()
{
  return indexDefinition->getAnalyzerName();
}

void MySqlTableIndex::setAnalyzerName(SQLName_ptr analyzerName)
{
  this->indexDefinition->setAnalyzerName(analyzerName);
}
